/*
 * Copyright (C) 2010 TopCoder Inc., All Rights Reserved.
 */
package com.hercules.xre.protocol.testing.netty.websocket;

import javax.net.ssl.SSLEngine;

import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.ssl.SslHandler;

import com.hercules.xre.protocol.testing.EventHandler;
import com.hercules.xre.protocol.testing.Helper;
import com.hercules.xre.protocol.testing.netty.ChannelExceptionHandler;
import com.hercules.xre.protocol.testing.netty.XREEventsHandler;
import com.hercules.xre.protocol.testing.netty.tcp.TCPConnectionChannelPipelineFactory;
import com.topcoder.util.log.Log;
import com.topcoder.util.log.LogManager;

/**
 * <p>
 * Pipeline Factory that is responsible for initialization of channel handlers pipeline for web socket based
 * connections.
 * </p>
 * <p>
 * <strong>Thread-safety:</strong> This class is thread safe as it's immutable.
 * </p>
 *
 * @author TCSASSEMBLER
 * @version 1.0
 */
public class WebSocketConnectionChannelPipelineFactory implements ChannelPipelineFactory {
    /**
     * <p>
     * Logger that is used to log activities and errors.
     * </p>
     * <p>
     * It is initialized to LogManager.getLog(TCPConnectionChannelPipelineFactory.class.getName()). Can not be null,
     * never changes after initialization.
     * </p>
     */
    private static final Log LOGGER = LogManager.getLog(TCPConnectionChannelPipelineFactory.class.getName());

    /**
     * <p>
     * Event handler that will be used by channel handlers to inform application about communication events.
     * </p>
     * <p>
     * It is set in the constructor and never changes after that.
     * </p>
     */
    private final EventHandler eventHandler;
    /**
     * <p>
     * Connection manager that will be used by channel. It is set in the constructor and never changes after that.
     * </p>
     */
    private final WebSocketProtocolTestingConnectionManager connectionManager;

    /**
     * <p>
     * Creates new instance, it simply sets passed parameters to appropriate fields.
     * </p>
     *
     * @param eventHandler
     *            event handler that will be used to report detected events, can't be null
     * @param connectionManager
     *            connection manager, can't be null
     * @throws IllegalArgumentException
     *             when any attribute is invalid
     */
    public WebSocketConnectionChannelPipelineFactory(WebSocketProtocolTestingConnectionManager connectionManager,
            EventHandler eventHandler) {
        final String signature = "WebSocketConnectionChannelPipelineFactory#ctor";
        Helper.logEnter(signature, new String[] {"connectionManager, eventHandler" }, new Object[] {
            connectionManager, eventHandler }, LOGGER);
        try {
            Helper.checkNull(connectionManager, "connectionManager", signature, LOGGER);
            Helper.checkNull(eventHandler, "eventHandler", signature, LOGGER);
            this.connectionManager = connectionManager;
            this.eventHandler = eventHandler;
        } finally {
            Helper.logExit(signature, null, LOGGER);
        }
    }

    /**
     * <p>
     * Creates new channel pipeline that will be used to handle TCP communication.
     * </p>
     *
     * @return ChannelPipeline instance.
     */
    public ChannelPipeline getPipeline() {
        final String signature = "WebSocketConnectionChannelPipelineFactory#getPipeline";
        Helper.logEnter(signature, null, null, LOGGER);

        ChannelPipeline pipeline = Channels.pipeline();
        // add a handler for exceptions
        pipeline.addLast("exceptionsHandler", new ChannelExceptionHandler());

        // if encryption is to be used add SSL handler as a first handler
        SSLEngine engine = connectionManager.getSSLEngine();
        if (engine != null) {
            pipeline.addLast("ssl", new SslHandler(engine));
        }

        //the decoder of the HTTP request. Note that before the handshaking, the communication is over HTTP.
        //The HTTP decoder and encoder will be removed after the handshaking is done.
        pipeline.addLast("decoder", new HttpRequestDecoder());
        //WebSocketServerHandler is responsible for the handshaking
        pipeline.addLast("handler", new WebSocketServerHandler());

        //the encoder of the HTTP Response.
        pipeline.addLast("encoder", new HttpResponseEncoder());

        //web socket frame adapter
        pipeline.addLast("adapter", new WebSocketFrameBufferAdapter(connectionManager));

        // handler that is responsible for dispatching events
        pipeline.addLast("XREEventsHandler", new XREEventsHandler(connectionManager, eventHandler));

        return Helper.logExit(signature, pipeline, LOGGER);
    }
}
